/**
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.wsgi.activity.registry;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import cc.aileron.accessor.PojoAccessor;
import cc.aileron.commons.instance.InstanceFactory;
import cc.aileron.commons.utils.SkipList;
import cc.aileron.wsgi.WsgiHttpCategory;
import cc.aileron.wsgi.activity.WsgiController;
import cc.aileron.wsgi.activity.WsgiProcess;
import cc.aileron.wsgi.activity.WsgiValidator;
import cc.aileron.wsgi.container.RegistryCondition;
import cc.aileron.wsgi.container.WsgiContainer;

import com.google.inject.Provider;

/**
 * @author Aileron
 * @param <Resource>
 */
public class WsgiControllerCreatorImpl<Resource> implements
    WsgiControllerCreator<Resource>
{
    @Override
    public WsgiControllerCreator<Resource> category(final WsgiHttpCategory category)
    {
        registryCondition.category = category;
        return this;
    }

    @Override
    public WsgiControllerCreator<Resource> exception(final Class<Exception> exception,
            final WsgiProcess<Resource> process)
    {
        this.exceptionMappings.put(exception, process);
        return this;
    }

    @Override
    public WsgiControllerCreator<Resource> extension(final String extension)
    {
        registryCondition.extension = extension;
        return this;

    }

    @Override
    public WsgiControllerCreator<Resource> isThrough(final boolean isThrough)
    {
        registryCondition.isThrough = isThrough;
        return this;

    }

    @Override
    public WsgiControllerCreator<Resource> overrideKey(final String overrideKey)
    {
        registryCondition.overrideKey = overrideKey;
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final Class<? extends WsgiProcess<? super Resource>> process)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiProcess<Resource>) instanceFactory.create(process)));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final Class<? extends WsgiValidator<? super Resource>> validator,
            final Class<? extends WsgiProcess<? super Resource>> process)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiValidator<Resource>) instanceFactory.create(validator),
                (WsgiProcess<Resource>) instanceFactory.create(process)));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final Class<? extends WsgiValidator<? super Resource>> validator,
            final WsgiProcess<? super Resource> process)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiValidator<Resource>) instanceFactory.create(validator),
                (WsgiProcess<Resource>) process));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final Class<? extends WsgiValidator<? super Resource>> validator,
            final WsgiTransitionCategory transition,
            final String... value)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiValidator<Resource>) instanceFactory.create(validator),
                transition.<Resource> process(value)));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final WsgiProcess<? super Resource> process)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiProcess<Resource>) process));
        return this;
    }

    @Override
    public WsgiControllerCreator<Resource> process(final WsgiTransitionCategory transition,
            final String... value)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                transition.<Resource> process(value)));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final WsgiValidator<? super Resource> validator,
            final Class<? extends WsgiProcess<? super Resource>> process)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiValidator<Resource>) validator,
                (WsgiProcess<Resource>) instanceFactory.create(process)));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final WsgiValidator<? super Resource> validator,
            final WsgiProcess<? super Resource> process)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiValidator<Resource>) validator,
                (WsgiProcess<Resource>) process));
        return this;
    }

    @SuppressWarnings("unchecked")
    @Override
    public WsgiControllerCreator<Resource> process(final WsgiValidator<? super Resource> validator,
            final WsgiTransitionCategory transition,
            final String... value)
    {
        processList.add(new WsgiValidatorAndProcess<Resource>(
                (WsgiValidator<Resource>) validator,
                transition.<Resource> process(value)));
        return this;
    }

    @Override
    public void registry(final WsgiContainer container)
    {
        container.put(createCondition(), createController());
    }

    @Override
    public WsgiControllerCreator<Resource> requestParameterKeys(final String... keys)
    {
        requestParameterKeys.addAll(Arrays.asList(keys));
        return this;
    }

    @Override
    public WsgiControllerCreator<Resource> uri(final String uri)
    {
        registryCondition.uri = uri;
        return this;
    }

    private RegistryCondition createCondition()
    {
        return registryCondition;
    }

    /**
     * @return controller
     */
    private WsgiController createController()
    {
        final HashMap<Class<Exception>, WsgiProcess<Resource>> exception = exceptionMappings;
        final List<WsgiValidatorAndProcess<Resource>> process = processList;
        final List<String> requestParameter = requestParameterKeys;
        requestParameterKeys.addAll(new UriTemplate(registryCondition.uri).extract());
        return new WsgiControllerImple<Resource>(accessorProvider)
        {
            @Override
            protected HashMap<Class<Exception>, WsgiProcess<Resource>> exceptionMappings()
            {
                return exception;
            }

            @Override
            protected List<WsgiValidatorAndProcess<Resource>> processList()
            {
                return process;
            }

            @Override
            protected List<String> requestParameterKeys()
            {
                return requestParameter;
            }
        };
    }

    /**
     * constractor
     * 
     * @param instanceFactory
     * 
     * @param accessorProvider
     */
    public WsgiControllerCreatorImpl(
        final InstanceFactory instanceFactory,
        final Provider<PojoAccessor<Resource>> accessorProvider)
    {
        this.instanceFactory = instanceFactory;
        this.accessorProvider = accessorProvider;
        this.registryCondition = new RegistryCondition();
        this.exceptionMappings = new HashMap<Class<Exception>, WsgiProcess<Resource>>();
        this.processList = new SkipList<WsgiValidatorAndProcess<Resource>>();
        this.requestParameterKeys = new SkipList<String>();
    }

    private final InstanceFactory instanceFactory;

    /**
     * registryCondition
     */
    private final RegistryCondition registryCondition;

    /**
     * accessorProvider
     */
    private final Provider<PojoAccessor<Resource>> accessorProvider;

    /**
     * exceptionMappings
     */
    private final HashMap<Class<Exception>, WsgiProcess<Resource>> exceptionMappings;

    /**
     * processList
     */
    private final List<WsgiValidatorAndProcess<Resource>> processList;

    /**
     * requestParameterKeys
     */
    private final List<String> requestParameterKeys;
}
